#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#define exit_if(r, ...)                                            \
  if (r) {                                                         \
    printf(__VA_ARGS__);                                           \
    printf("error no: %d error msg %s\n", errno, strerror(errno)); \
    exit(1);                                                       \
  }

#ifndef __APPLE__
#include <sys/epoll.h>
const int kReadEvent = EPOLLIN;
const int kWriteEvent = EPOLLOUT;
#else
#include <sys/event.h>
const int kReadEvent = 1;
const int kWriteEvent = 2;
#endif

void handleWrite(int efd, int fd);
void handleRead(int efd, int fd);
void handleAccept(int efd, int fd);

void SetNonBlock(int fd) {
  int flags = fcntl(fd, F_GETFL, 0);
  exit_if(flags < 0, "fcntl failed");
  int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
  exit_if(r < 0, "fcntl failed");
}
void SetReuseaddr(int fd) {
  int reuse = 1;
  setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof reuse);
}

#ifdef __linux__
void updateEvents(int epfd, int fd, int events, bool modify) {
  int ret;
  struct epoll_event event;
  event.data.fd = fd;
  event.events = events;
  if (!modify) {
    ret = epoll_ctl(epfd, EPOLL_CTL_ADD, event.data.fd, &event);
    exit_if(ret == -1, "epoll_ctl failed!");
  } else {
    ret = epoll_ctl(epfd, EPOLL_CTL_MOD, event.data.fd, &event);
    exit_if(ret == -1, "epoll_ctl failed!");
  }
  printf("%s fd %d events read %d write %d\n", modify ? "mod" : "add", fd,
         events & kReadEvent, events & kWriteEvent);
}
void loop_once(int efd, int lfd, int waitms) {
  struct timespec timeout;
  timeout.tv_sec = waitms / 1000;
  timeout.tv_nsec = (waitms % 1000) * 1000 * 1000;
  const int kMaxEvents = 20;

  struct epoll_event events[kMaxEvents];
  int n = epoll_wait(efd, events, kMaxEvents, -1);
  printf("epoll_wait return %d\n", n);
  for (int i = 0; i < n; i++) {
    int fd = events[i].data.fd;
    if (events[i].events == EPOLLIN)
      if (fd == lfd)
        handleAccept(efd, fd);
      else
        handleRead(efd, fd);
    else if (events[i].events == EPOLLOUT)
      handleWrite(efd, fd);
    else
      exit_if(1, "unknown event");
  }
}
#else
void updateEvents(int efd, int fd, int events, bool modify) {
  struct kevent ev[2];
  int n = 0;
  if (events & kReadEvent) {
    EV_SET(&ev[n++], fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0,
           (void *)(intptr_t)fd);
  } else if (modify) {
    EV_SET(&ev[n++], fd, EVFILT_READ, EV_DELETE, 0, 0, (void *)(intptr_t)fd);
  }
  if (events & kWriteEvent) {
    EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_ADD | EV_ENABLE, 0, 0,
           (void *)(intptr_t)fd);
  } else if (modify) {
    EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_DELETE, 0, 0, (void *)(intptr_t)fd);
  }
  printf("%s fd %d events read %d write %d\n", modify ? "mod" : "add", fd,
         events & kReadEvent, events & kWriteEvent);
  int r = kevent(efd, ev, n, NULL, 0, NULL);
  exit_if(r, "kevent failed ");
}

void loop_once(int efd, int lfd, int waitms) {
  struct timespec timeout;
  timeout.tv_sec = waitms / 1000;
  timeout.tv_nsec = (waitms % 1000) * 1000 * 1000;
  const int kMaxEvents = 20;
  struct kevent activeEvs[kMaxEvents];
  int n = kevent(efd, NULL, 0, activeEvs, kMaxEvents, &timeout);
  printf("epoll_wait return %d\n", n);
  for (int i = 0; i < n; i++) {
    int fd = (int)(intptr_t)activeEvs[i].udata;
    int events = activeEvs[i].filter;
    if (events == EVFILT_READ) {
      if (fd == lfd) {
        handleAccept(efd, fd);
      } else {
        handleRead(efd, fd);
      }
    } else if (events == EVFILT_WRITE) {
      handleWrite(efd, fd);
    } else {
      exit_if(1, "unknown event");
    }
  }
}
#endif
void handleAccept(int efd, int fd) {
  struct sockaddr_in raddr;
  socklen_t rsz = sizeof(raddr);
  int cfd = accept(fd, (struct sockaddr *)&raddr, &rsz);
  exit_if(cfd < 0, "accept failed");
  sockaddr_in peer, local;
  socklen_t alen = sizeof(peer);
  int r = getpeername(cfd, (sockaddr *)&peer, &alen);
  exit_if(r < 0, "getpeername failed");
  printf("accept a connection from %s\n", inet_ntoa(raddr.sin_addr));
  SetNonBlock(cfd);
  updateEvents(efd, cfd, kReadEvent | kWriteEvent, false);
}

void handleRead(int efd, int fd) {
  char buf[4096];
  int n = 0;
  while ((n = ::read(fd, buf, sizeof buf)) > 0) {
    printf("read %d bytes by server\n", n);
    int r = ::write(fd, buf, n);  //写出读取的数据
    //实际应用中，写出数据可能会返回EAGAIN，此时应当监听可写事件，当可写时再把数据写出
    exit_if(r <= 0, "write error");
    printf("%d bytes sended by server\n", r);
  }
  if (n < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) return;
  exit_if(n < 0, "read error");  //实际应用中，n<0应当检查各类错误，如EINTR
  printf("fd %d closed\n", fd);
  close(fd);
}

void handleWrite(int efd, int fd) {
  //实际应用应当实现可写时写出数据，无数据可写才关闭可写事件
  updateEvents(efd, fd, kReadEvent, true);
}
int Init_Socket() {
  short port = 9999;
  int listenfd = socket(AF_INET, SOCK_STREAM, 0);
  exit_if(listenfd < 0, "socket failed");
  struct sockaddr_in addr;
  memset(&addr, 0, sizeof addr);
  addr.sin_family = AF_INET;
  addr.sin_port = htons(port);
  addr.sin_addr.s_addr = INADDR_ANY;
  int r = ::bind(listenfd, (struct sockaddr *)&addr, sizeof(struct sockaddr));
  exit_if(r, "bind to 0.0.0.0:%d failed %d %s", port, errno, strerror(errno));
  r = listen(listenfd, 20);
  exit_if(r, "listen failed %d %s", errno, strerror(errno));
  printf("fd %d listening at %d\n", listenfd, port);

  SetNonBlock(listenfd);
  SetReuseaddr(listenfd);
  return listenfd;
}

int main() {
  int epollfd = epoll_create(5);
  exit_if(epollfd < 0, "epoll_create failed");

  int lfd = Init_Socket();
  updateEvents(epollfd, lfd, kReadEvent, false);

  for (;;) {  //实际应用应当注册信号处理函数，退出时清理资源
    loop_once(epollfd, lfd, 10000);
  }
  return 0;
}
